package client.backend.user;

import client.backend.Connection;
import client.backend.account.Account;
import client.backend.order.Order;
import server.conn.admin.AdminThread;
import server.conn.user.UserConnectionThread;
import server.conn.ConnectionThread;

import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.LinkedList;

/**
 * User stores information about the current client.
 *
 */
public class User {
	public static final String ID = "ID";
	public static final String USERNAME = "USERNAME";
	public static final String REALNAME = "REALNAME";
	public static final String EMAIL = "EMAIL";
	public static final String LEVERAGE = "LEVERAGE";
	public static final String ACTIVE = "ACTIVE";
	public static final String ADMIN = "ADMIN";
	
	public static final int CLOSED_LIMIT = 10;
	
	private int id;
	private String userName;
	private String realName;
	private String eMail;
	private float leverage;
	private boolean active;
	
	private LinkedList<Order> openOrders;
	private LinkedList<Order> closedOrders;
	private HashMap<String, Account> accounts;
	private Boolean admin;
	
	/**
	 * Creates a blank user.
	 * @throws IOException
	 */
	public User() throws IOException {
		openOrders = new LinkedList<Order>();
		closedOrders = new LinkedList<Order>();
		
		updateInfo();
		initAccounts();
	}
	
	/**
	 * Constructor for use by admins
	 * @param idno UserId
	 * @throws IOException
	 */
	public User(int idno) throws IOException {
		openOrders = new LinkedList<Order>();
		closedOrders = new LinkedList<Order>();
		
		String response = Connection.getInstance().readFromServer(AdminThread.USER_DETAIL+AdminThread.SPLIT+idno);
		StringTokenizer strTok = new StringTokenizer(response, ConnectionThread.SPLIT);
		
		id = new Integer(strTok.nextToken());
		userName = strTok.nextToken();
		realName = strTok.nextToken();
		eMail = strTok.nextToken();
		leverage = new Float(strTok.nextToken());
		active = new Boolean(strTok.nextToken());
		admin = new Boolean(strTok.nextToken());
	}
	
	/**
	 * Private method for updating user information.
	 * @throws IOException
	 */
	private final void updateInfo() throws IOException {
		String response = Connection.getInstance().readFromServer(UserConnectionThread.USER_GET_INFO+"");
		
		StringTokenizer strTok = new StringTokenizer(response, ConnectionThread.SPLIT);
		
		id = new Integer(strTok.nextToken());
		userName = strTok.nextToken();
		realName = strTok.nextToken();
		eMail = strTok.nextToken();
		leverage = new Float(strTok.nextToken());
		active = new Boolean(strTok.nextToken());
		admin = new Boolean(strTok.nextToken());
	}
	
	/**
	 * Private method for updating open orders.
	 */
	private void updateOpenOrders() {
		openOrders.clear();
		String[] capture = Connection.getInstance().readArray(UserConnectionThread.ORDER_GET_OPEN+"");
		for(String line : capture){
			openOrders.add(new Order(line));
		}
	}
	
	/**
	 * Private method for updating closed orders.
	 */
	private void updateClosedOrders() {
		closedOrders.clear();
		String[] capture = Connection.getInstance().readArray(UserConnectionThread.ORDER_GET_CLOSED+ConnectionThread.SPLIT+CLOSED_LIMIT);
		for(String line : capture){
			closedOrders.add(new Order(line));
		}
	}
	
	/**
	 * Checks a string to see if it is a valid float
	 * @param input
	 * @return
	 */
	private boolean checkSTF(String input) {
		boolean seenDot = false;
		
		for(int i=0;i<input.length();i++) {
			if (input.charAt(i)=='.' && seenDot)
				return false;
			else if (input.charAt(i)=='.')
				seenDot = true;
			else if(!Character.isDigit(input.charAt(i)))
				return false;
		}
		return true;
	}
	
	/**
	 * Attempts to create new limit order and returns true if successful.
	 * @param currPairs	Currency Pair of order. Ex. currPairs = "CADUSD".
	 * @param buySell	Either "Buy" or "Sell".
	 * @param amount	Amount of currency to be transacted. (Second currency in pair)
	 * @param limPrice	Limit price of order.
	 * @return			Returns whether or not the order creation was successful.
	 * @see	server.order.LimitOrder
	 */
	public boolean createLO(String currPairs, String buySell, String amount, String limPrice) {
		try {
			String curr1 = currPairs.substring(0,3);
			String curr2 = currPairs.substring(3,6);
			if (!checkSTF(amount) || !checkSTF(limPrice))
				return false;
			String response = Connection.getInstance().readFromServer(UserConnectionThread.ORDER_OPEN+ConnectionThread.SPLIT
							  +curr1+ConnectionThread.SPLIT+curr2+ConnectionThread.SPLIT
							  +limPrice+ConnectionThread.SPLIT+amount+ConnectionThread.SPLIT
							  +(buySell.equalsIgnoreCase("buy")?Order.BUY:Order.SELL)+ConnectionThread.SPLIT
							  +Order.ORD_LIMIT);
			if (!response.startsWith(ConnectionThread.ERROR))
				return true;
		}
		catch (Exception e) {}
		return false;
	}
	
	/**
	 * Attempts to create new trailing stop order and returns true if successful.
	 * @param currPairs	Currency Pair of order. Ex. currPairs = "CADUSD".
	 * @param buySell	Either "Buy" or "Sell".
	 * @param amount	Amount of currency to be transacted. (Second currency in pair)
	 * @param price		Limit price of order.
	 * @param percent	Trailing percent of order.
	 * @return			Returns whether or not the order creation was successful.
	 * @see server.order.TrailingStopOrder
	 */
	public boolean createTS(String currPairs, String buySell, String amount, String price, String percent) {
		try {
			String curr1 = currPairs.substring(0,3);
			String curr2 = currPairs.substring(3,6);
			if (!checkSTF(amount) || !checkSTF(price) || !checkSTF(percent))
				return false;
			String response = Connection.getInstance().readFromServer(UserConnectionThread.ORDER_OPEN+ConnectionThread.SPLIT
							  +curr1+ConnectionThread.SPLIT+curr2+ConnectionThread.SPLIT
							  +price+ConnectionThread.SPLIT+amount+ConnectionThread.SPLIT
							  +(buySell.equalsIgnoreCase("buy")?Order.BUY:Order.SELL)+ConnectionThread.SPLIT
							  +Order.ORD_TRAILING_STOP+ConnectionThread.SPLIT+percent);
			if (!response.startsWith(ConnectionThread.ERROR))
				return true;
			else
				return false;
		} catch (IOException e) {
			return false;
		}
	}
	
	/**
	 * Private method for initializing accounts.
	 */
	private void initAccounts() {
		accounts = new HashMap<String, Account>();
		
		String[] accountResponse = Connection.getInstance().readArray(UserConnectionThread.ACCOUNT_GET_ALL+"");
		
		for (String a : accountResponse) {
			StringTokenizer strTok = new StringTokenizer(a, ConnectionThread.SPLIT);
			String sym = strTok.nextToken();
			float amount = new Float(strTok.nextToken());
			long time = new Long(strTok.nextToken());
			accounts.put(sym, new Account(sym, amount, time));
		}
	}
	
	/**
	 * Static method for generating list of users on ACE.
	 * @return	Array of users.
	 * @throws IOException
	 */
	public static User[] getUsers() throws IOException{
		String[] response = Connection.getInstance().readArray(AdminThread.USERS_LIST+"");
		User users[] = new User[response.length];
		String[] split;
		for(int i=0; i<response.length; i++){
			split = response[i].split(AdminThread.SPLIT);
			users[i] = new User(new Integer(split[1]));
		}
		return users;
	}
	/**
	 * Edit a user
	 * @param username
	 * @param name
	 * @param email
	 * @param leverage
	 */
	public void editUser(String username, String name, String email, float leverage){
		
	}
	
	/**
	 * Getter method for account.
	 * @param sym	Symbol of the account.
	 * @return		The account.
	 */
	public Account getAccount(String sym) {	return (accounts.containsKey(sym)) ? accounts.get(sym) : null; }
	
	/**
	 * Getter method for open orders.
	 * @return	Open orders.
	 */
	public LinkedList<Order> getOpenOrders() { updateOpenOrders(); return openOrders; }
	
	/**
	 * Getter method for closed orders.
	 * @return Closed orders.
	 */
	public LinkedList<Order> getClosedOrders() { updateClosedOrders(); return closedOrders; }
	
	/**
	 * Getter method for ID.
	 * @return	ID.
	 */
	public int getID() { return id; }
	
	/**
	 * Getter method for username. 
	 * @return	Username.
	 */
	public String getUserName() { return userName; }
	
	/**
	 * Getter method for real name.
	 * @return	Real name.
	 */
	public String getRealName() { return realName; }
	
	/**
	 * Getter method for email.
	 * @return	Email.
	 */
	public String getEMail() { return eMail; }
	
	/**
	 * Getter method for leverage.
	 * @return	Leverage.
	 */
	public float getLeverage() { return leverage; }
	
	/**
	 * Getter method for active.
	 * @return	Active.
	 */
	public boolean isActive() { return active; }
}
